# Win32TrayIcon.py
# Copyright (C) otacon 2010 <orfeo18@gmail.com>
# 
# AdSense-Monitor is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# AdSense-Monitor is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.
import win32gui
import win32con
class Win32TrayIcon():
    """
    tray icon for AdSense Library
    """
    GWL_WNDPROC = -4
    GWL_EXSTYLE = -20
    IDI_APPLICATION = 32512
    LWA_ALPHA = 0x02
    WM_TASKBARCREATED = win32gui.RegisterWindowMessage('TaskbarCreated')
    WM_USER = 1024
    WM_TRAYMESSAGE = WM_USER + 20
    WS_EX_LAYERED = 0x80000
    WM_LBUTTONUP = 0x0202
    WM_RBUTTONUP = 0x0205

    def __init__(self , icon, tray, gtk_window):
        """
        constructor
        """
        self.menu = None
        # WINDOWS BINDINGS
        self._window = gtk_window
        self._hwnd = gtk_window.window.handle
        self._oldwndproc = win32gui.SetWindowLong(self._hwnd, Win32TrayIcon.GWL_WNDPROC,
                                                  self._wndproc)
        self._message_map = {}
        self._id = 0
        self._flags = win32gui.NIF_MESSAGE | win32gui.NIF_ICON
        self._callbackmessage = Win32TrayIcon.WM_TRAYMESSAGE

        hinst = win32gui.GetModuleHandle(None) 
        flag = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE 
        self._hicon = win32gui.LoadImage(hinst, icon , win32con.IMAGE_ICON, 0, 0, flag)
        
        win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, self._get_nid())
        self.message_map({Win32TrayIcon.WM_TRAYMESSAGE: self._on_popup}) 
        self.message_map({Win32TrayIcon.WM_TASKBARCREATED: self._redraw})
        

    #################
    #WINDOWS METHODS#
    #################
    def set_icon( self, icon ):
        hinst = win32gui.GetModuleHandle(None)
        flag = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
        self._hicon = win32gui.LoadImage(hinst, icon , win32con.IMAGE_ICON, 0, 0, flag)
        self._flags = win32gui.NIF_ICON
        win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, self._get_nid())
        self._flags = win32gui.NIF_MESSAGE | win32gui.NIF_ICON
        return
    
    def message_map(self, msg_map={}):
        """ Maps message processing to callback functions ala win32gui. """
        if msg_map:
            if self._message_map:
                duplicatekeys = [key for key in msg_map.keys()
                                 if self._message_map.has_key(key)]
                
                for key in duplicatekeys:
                    new_value = msg_map[key]
                    
                    if isinstance(new_value, list):
                        raise TypeError('Dict cannot have list values')
                    
                    value = self._message_map[key]
                    
                    if new_value != value:
                        new_value = [new_value]
                        
                        if isinstance(value, list):
                            value += new_value
                        else:
                            value = [value] + new_value
                        
                        msg_map[key] = value
            self._message_map.update(msg_map)


    def message_unmap(self, msg, callback=None):
        if self._message_map.has_key(msg):
            if callback:
                cblist = self._message_map[key]
                if isinstance(cblist, list):
                    if not len(cblist) < 2:
                        for i in range(len(cblist)):
                            if cblist[i] == callback:
                                del self._message_map[key][i]
                                return
            del self._message_map[key]

    def _get_nid(self):
        """ Function to initialise & retrieve the NOTIFYICONDATA Structure. """
        nid = (self._hwnd, self._id, self._flags, self._callbackmessage,
               self._hicon)
        nid = list(nid)

        if not hasattr(self, '_tip'): self._tip = ''
        nid.append(self._tip)

        if not hasattr(self, '_info'): self._info = ''
        nid.append(self._info)
            
        if not hasattr(self, '_timeout'): self._timeout = 0
        nid.append(self._timeout)

        if not hasattr(self, '_infotitle'): self._infotitle = ''
        nid.append(self._infotitle)
            
        if not hasattr(self, '_infoflags'):self._infoflags = win32gui.NIIF_NONE
        nid.append(self._infoflags)

        return tuple(nid)


    def _wndproc(self, hwnd, msg, wparam, lparam):
        """ A WINDPROC to process window messages. """
        if self._message_map.has_key(msg):
            callback = self._message_map[msg]
            if isinstance(callback, list):
                for cb in callback:
                    apply(cb, (hwnd, msg, wparam, lparam))
            else:
                apply(callback, (hwnd, msg, wparam, lparam))

        return win32gui.CallWindowProc(self._oldwndproc, hwnd, msg, wparam,
                                       lparam)
    
    def remove(self):
        """ Removes the tray icon. """
        win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, self._get_nid())


    def set_tooltip(self, tooltip):
        """ Sets the tray icon tooltip. """
        self._flags = self._flags | win32gui.NIF_TIP
        self._tip = tooltip
        win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, self._get_nid())
        
        
    def show_notify(self, message):
        """ Shows a balloon tooltip from the tray icon. """                    
        self._flags = self._flags | win32gui.NIF_INFO
        self._infotitle = 'Adsense-Monitor'
        self._info = message
        #self._timeout = 10000
        #self._infoflags = icon

        win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, self._get_nid())

    def set_menu(self, menu):
        self.menu = menu
        self.menu.show_all()

    def _redraw(self, *args):
        """ Redraws the tray icon. """
        self.remove
        win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, self._get_nid())

    def _on_popup(self, hwnd, message, wparam, lparam):
        """
        callback called when the popup of the status icon is activated
        (usually through right-clicking the status icon)
        """
        if lparam == Win32TrayIcon.WM_RBUTTONUP:
            self.menu.popup(None, None, None, 0, 0)
        elif lparam == Win32TrayIcon.WM_LBUTTONUP:
            self.menu.popdown()
